package com.drpclub.gamebackend.common.utils.log;


import cn.hutool.json.JSONUtil;
import com.drpclub.gamebackend.exception.IgnoreException;
import com.drpclub.gamebackend.exception.MyAccessException;
import com.drpclub.gamebackend.common.utils.NetworkResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

import static com.drpclub.gamebackend.common.utils.DrpUtils.getIpAddr;
import static com.drpclub.gamebackend.common.utils.DrpUtils.getUrl;
import static com.drpclub.gamebackend.common.utils.DrpUtils.getUserAgent;
import static com.drpclub.gamebackend.common.utils.DrpUtils.getUsernameFromRequest;
import static com.drpclub.gamebackend.common.utils.SpringContextUtils.isDev;

/**
 * @author dongruipeng
 * @Descrpition monitor log can use this class
 * @date 2019year 12month04day  15:05:08
 */
@Slf4j
public final class RsHelper<T> {

    public static <T> NetworkResult<T> success(T data) {
        final LogCollectInfo info = printLog(null);
        if (info.getErrorCode() != NetworkResult.SUCCESS_CODE) {
            return new NetworkResult<T>(info.getErrorCode(), info.getErrorMsg());
        }
        return new NetworkResult<T>(data);
    }


    private static LogCollectInfo printLog(Throwable throwable) {
        final ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        final HttpServletRequest request = requestAttributes.getRequest();
        final LogCollectInfo info = initInfo(request, throwable, NetworkResult.SUCCESS_CODE, HttpStatus.OK, NetworkResult.SUCCESS_MSG);
        log.info(JSONUtil.toJsonStr(info));
        return info;
    }

    /**
     * <h2>设置 LogCollectInfo 的初始值</h2>
     * o
     *
     * @param request HttpServletRequest
     * @return LogCollectInfo, 用于转换 Json 格式
     */
    private static LogCollectInfo initInfo(HttpServletRequest request, Throwable throwable, int errorCode, HttpStatus status, String errorMsg) {
        final LogCollectInfo logCollectInfo = new LogCollectInfo();
        logCollectInfo.setRequestUrl(getUrl(request));//url
        logCollectInfo.setIp(getIpAddr(request));//ip
        logCollectInfo.setUsername (getUsernameFromRequest (request));//UserId
        logCollectInfo.setAgent(getUserAgent(request));//User-Agent
        logCollectInfo.setRequestMethod(request.getMethod());//requestMethod
        logCollectInfo.setAction(request.getParameter("action"));//action
        final long requestTimeInMs = (long) request.getAttribute("requestTimeInMs");
        logCollectInfo.setRequestTimeInMs (requestTimeInMs);
        if (throwable != null) {
            setThrowableInfoErrorCodeAndStatus(throwable, logCollectInfo); //设置Exception 的信息
        } else {
            logCollectInfo.setResponseStatus(status.value());//获取返回response， 例如重定向302
            logCollectInfo.setErrorCode(errorCode);
            logCollectInfo.setErrorMsg(errorMsg);
        }
        logCollectInfo.setUseTimeInMs(System.currentTimeMillis() - requestTimeInMs);//UseTimeInMs
        return logCollectInfo;
    }

    private static void setThrowableInfoErrorCodeAndStatus(Throwable throwable, LogCollectInfo logCollectInfo) {
        final Class<? extends Throwable> ex = throwable.getClass();
        logCollectInfo.setExceptionType(ex.getName());
        logCollectInfo.setExceptionMsg(getExceptionMessage(throwable));//写入日志
        //根据ExceptionType 来设置对应的errorCode 与 responseStatus
        logCollectInfo.setErrorCode(NetworkResult.BAD_REQUEST_ERROR_CODE);//errorCode 默认 400
        logCollectInfo.setResponseStatus(HttpStatus.OK.value());//responseStatus 默认 400
        logCollectInfo.setErrorMsg(NetworkResult.BAD_REQUEST_ERROR_MSG); //返回前端
        if (ex.equals(IgnoreException.class)) {
            // status 200, errorCode 400, 非逻辑错误时，允许前端接收并处理的返回，请抛出此异常
            logCollectInfo.setErrorMsg(getExceptionMessage(throwable));
        } else if (ex.equals(MyAccessException.class)) { //status 404, errorCode 404
            logCollectInfo.setErrorCode(NetworkResult.ACCESS_CODE);
            logCollectInfo.setErrorMsg(NetworkResult.ACCESS_MSG);
        }
    }

    public static <T> ResponseEntity<NetworkResult<T>> error(Throwable throwable) {
        if(isDev()) {
            throwable.printStackTrace ();
        }
        final LogCollectInfo info = printLog(throwable);
        return response(new NetworkResult<>(info.getErrorCode(), info.getErrorMsg()));
    }

    /**
     * httpStatus aways ok
     */
    private static <T> ResponseEntity<T> response(T body) {
        return new ResponseEntity<T>(body, new HttpHeaders(), HttpStatus.OK);
    }

    private static String getExceptionMessage(Throwable throwable) {
        return throwable.getMessage() == null ? NetworkResult.BAD_REQUEST_ERROR_MSG : throwable.getMessage();
    }
}
